package com.ymx.darling.log.jdbc;

import com.ymx.darling.log.Log;
import com.ymx.darling.util.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;

/**
 * @author 爱Java小于
 */
public class ResultSetLogger extends BaseJdbcLogger implements InvocationHandler {
    private static final Set<Integer> BLOB_TYPES = new HashSet<>();
    private final Set<Integer> blobColumns = new HashSet<>();
    private boolean first = true;
    private int rows;
    private final ResultSet rs;

    private ResultSetLogger(ResultSet rs, Log statementLogger) {
        super(statementLogger);
        this.rs = rs;
    }

    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, params);
            } else {
                Object o = method.invoke(this.rs, params);
                if ("next".equals(method.getName())) {
                    if ((Boolean)o) {
                        ++this.rows;
                        if (this.isTraceEnabled()) {
                            ResultSetMetaData resultSetMetaData = this.rs.getMetaData();
                            int columnCount = resultSetMetaData.getColumnCount();
                            if (this.first) {
                                this.first = false;
                                this.printColumnHeaders(resultSetMetaData, columnCount);
                            }

                            this.printColumnValues(columnCount);
                        }
                    } else {
                        this.debug("      Total: " + this.rows, false);
                    }
                }

                this.clearColumnInfo();
                return o;
            }
        } catch (Throwable throwable) {
            throw ExceptionUtil.unwrapThrowable(throwable);
        }
    }


    private void printColumnHeaders(ResultSetMetaData resultSetMetaData, int columnCount) throws SQLException {
        StringJoiner row = new StringJoiner(", ", "    Columns: ", "");

        for(int i = 1; i <= columnCount; ++i) {
            if (BLOB_TYPES.contains(resultSetMetaData.getColumnType(i))) {
                this.blobColumns.add(i);
            }

            row.add(resultSetMetaData.getColumnLabel(i));
        }

        this.trace(row.toString(), false);
    }

    private void printColumnValues(int columnCount) {
        StringJoiner row = new StringJoiner(", ", "        Row: ", "");

        for(int i = 1; i <= columnCount; ++i) {
            try {
                if (this.blobColumns.contains(i)) {
                    row.add("<<BLOB>>");
                } else {
                    row.add(this.rs.getString(i));
                }
            } catch (SQLException var5) {
                row.add("<<Cannot Display>>");
            }
        }

        this.trace(row.toString(), false);
    }

    public static ResultSet newInstance(ResultSet rs, Log statementLogger) {
        InvocationHandler handler = new ResultSetLogger(rs, statementLogger);
        ClassLoader cl = ResultSet.class.getClassLoader();
        return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
    }

    static {
        BLOB_TYPES.add(-2);
        BLOB_TYPES.add(2004);
        BLOB_TYPES.add(2005);
        BLOB_TYPES.add(-16);
        BLOB_TYPES.add(-4);
        BLOB_TYPES.add(-1);
        BLOB_TYPES.add(2011);
        BLOB_TYPES.add(-3);
    }
}
